home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 August (Alt) / CHIP 2005-08.1.iso / program / guvenlik / syslinux-3.07.exe / menu / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2004-12-14  |  25.7 KB  |  920 lines

  1. /* -*- c -*- ------------------------------------------------------------- *
  2.  *
  3.  *   Copyright 2004 Murali Krishnan Ganapathy - All Rights Reserved
  4.  *
  5.  *   This program is free software; you can redistribute it and/or modify
  6.  *   it under the terms of the GNU General Public License as published by
  7.  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
  8.  *   Boston MA 02111-1307, USA; either version 2 of the License, or
  9.  *   (at your option) any later version; incorporated herein by reference.
  10.  *
  11.  * ----------------------------------------------------------------------- */
  12.  
  13. /* This program can be compiled for DOS with the OpenWatcom compiler
  14.  * (http://www.openwatcom.org/):
  15.  *
  16.  * wcl -3 -osx -mt getargs.c
  17.  */
  18.  
  19. #include "biosio.h"
  20. #include "string.h"
  21. #include "menu.h"
  22. #include "heap.h"
  23.  
  24. // Local Variables
  25. static pt_menusystem ms; // Pointer to the menusystem
  26. static char TITLESTR[] = "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy";
  27. static char TITLELONG[] = " TITLE too long ";
  28. static char ITEMLONG[] = " ITEM too long ";
  29. static char ACTIONLONG[] = " ACTION too long ";
  30. static char STATUSLONG[] = " STATUS too long ";
  31. static char EMPTYSTR[] = "";
  32.  
  33. /* Basic Menu routines */
  34.  
  35. // This is same as inputc except it honors the ontimeout handler
  36. // and calls it when needed. For the callee, there is no difference
  37. // as this will not return unless a key has been pressed.
  38. char getch(char *scan)
  39. {
  40.   unsigned long i;
  41.   TIMEOUTCODE c;
  42.  
  43.   // Wait until keypress if no handler specified
  44.   if (ms->ontimeout==NULL) return inputc(scan);
  45.  
  46.   while (1) // Forever do
  47.     {
  48.       for (i=0; i < ms->tm_numsteps; i++)
  49.     {
  50.       if (checkkbdbuf()) return inputc(scan);
  51.       sleep(ms->tm_stepsize);
  52.     }
  53.       c = ms->ontimeout();
  54.       switch(c)
  55.     {
  56.     case CODE_ENTER: // Pretend user hit enter
  57.       *scan = ENTERA;
  58.       return '\015'; // \015 octal = 13
  59.     case CODE_ESCAPE: // Pretend user hit escape
  60.       *scan = ESCAPE;
  61.       return '\033'; // \033 octal = 27
  62.     default:
  63.       break;
  64.     }
  65.     }
  66.   return 0;
  67. }
  68.  
  69. /* Print a menu item */
  70. /* attr[0] is non-hilite attr, attr[1] is highlight attr */
  71. void printmenuitem(const char *str,char* attr)
  72. {
  73.     char page = getdisppage();
  74.     char row,col;
  75.     int hlite=NOHLITE; // Initially no highlighting
  76.  
  77.     getpos(&row,&col,page);
  78.     while ( *str ) {
  79.       switch (*str) 
  80.     {
  81.     case '\b':
  82.       --col;
  83.       break;
  84.     case '\n':
  85.       ++row;
  86.       break;
  87.     case '\r':
  88.       col=0;
  89.       break;
  90.     case BELL: // No Bell Char
  91.       break;
  92.     case ENABLEHLITE: // Switch on highlighting
  93.       hlite = HLITE;
  94.       break;
  95.     case DISABLEHLITE: // Turn off highlighting
  96.       hlite = NOHLITE;
  97.       break;
  98.     default:
  99.       putch(*str, attr[hlite], page);
  100.       ++col;
  101.     }
  102.       if (col > getnumcols())
  103.     {
  104.       ++row;
  105.       col=0;
  106.     }
  107.       if (row > getnumrows())
  108.     {
  109.       scrollup();
  110.       row= getnumrows();
  111.     }
  112.       gotoxy(row,col,page);
  113.       str++;
  114.     }
  115. }
  116.  
  117. void drawbox(char top, char left, char bot, char right,char attr, char page)
  118. {
  119.   char x;
  120.     
  121.   // Top border
  122.   gotoxy(top,left,page);
  123.   cprint(TOPLEFT,attr,1,page);
  124.   gotoxy(top,left+1,page);
  125.   cprint(TOP,attr,right-left,page);
  126.   gotoxy(top,right,page);
  127.   cprint(TOPRIGHT,attr,1,page);
  128.   // Bottom border
  129.   gotoxy(bot,left,page);
  130.   cprint(BOTLEFT,attr,1,page);
  131.   gotoxy(bot,left+1,page);
  132.   cprint(BOT,attr,right-left,page);
  133.   gotoxy(bot,right,page);
  134.   cprint(BOTRIGHT,attr,1,page);
  135.   // Left & right borders
  136.   for (x=top+1; x < bot; x++)
  137.     {
  138.       gotoxy(x,left,page);
  139.       cprint(LEFT,attr,1,page);
  140.       gotoxy(x,right,page);
  141.       cprint(RIGHT,attr,1,page);
  142.     }
  143. }
  144.  
  145. int next_visible(pt_menu menu, int index) // Return index of next visible
  146. {
  147.   int ans;
  148.   if (index < 0) ans = 0 ;
  149.   else if (index >= menu->numitems) ans = menu->numitems-1;
  150.   else ans = index;
  151.   while ((ans < menu->numitems-1) && 
  152.      ((menu->items[ans]->action == OPT_INVISIBLE) || 
  153.       (menu->items[ans]->action == OPT_SEP))) 
  154.     ans++;
  155.   return ans;
  156. }
  157.  
  158. int prev_visible(pt_menu menu, int index) // Return index of next visible
  159. {
  160.   int ans;
  161.   if (index < 0) ans = 0;
  162.   else if (index >= menu->numitems) ans = menu->numitems-1;
  163.   else ans = index;
  164.   while ((ans > 0) && 
  165.      ((menu->items[ans]->action == OPT_INVISIBLE) ||
  166.       (menu->items[ans]->action == OPT_SEP))) 
  167.     ans--;
  168.   return ans;
  169. }
  170.  
  171. int find_shortcut(pt_menu menu,char shortcut, int index) 
  172. // Find the next index with specified shortcut key
  173. {
  174.   int ans;
  175.   pt_menuitem mi;
  176.  
  177.   // Garbage in garbage out
  178.   if ((index <0) || (index >= menu->numitems)) return index; 
  179.   ans = index+1;
  180.   // Go till end of menu
  181.   while (ans < menu->numitems)     
  182.     {
  183.       mi = menu->items[ans];
  184.       if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
  185.       || (mi->shortcut != shortcut))
  186.     ans ++;
  187.       else return ans;
  188.     }
  189.   // Start at the beginning and try again
  190.   ans = 0;
  191.   while (ans < index)
  192.     {
  193.       mi = menu->items[ans];
  194.       if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
  195.       || (mi->shortcut != shortcut))
  196.     ans ++;
  197.       else return ans;
  198.     }
  199.   return index; // Sorry not found
  200. }
  201.  
  202. void printmenu(pt_menu menu, int curr, char top, char left)
  203. {
  204.   int x,row; // x = index, row = position from top
  205.   int numitems,menuwidth;
  206.   char fchar[5],lchar[5]; // The first and last char in for each entry
  207.   const char *str;  // and inbetween the item or a seperator is printed
  208.   char *attr;  // attribute attr
  209.   char sep[MENULEN];// and inbetween the item or a seperator is printed
  210.   pt_menuitem ci;
  211.   
  212.   calc_visible(menu);
  213.   numitems = menu->numvisible;
  214.   menuwidth = menu->menuwidth+3;
  215.   clearwindow(top,left-2, top+numitems+1, left+menuwidth+1,
  216.           ms->menupage, ms->fillchar, ms->shadowattr);
  217.   drawbox(top-1, left-3, top+numitems, left+menuwidth, 
  218.       ms->normalattr[NOHLITE], ms->menupage);
  219.   memset(sep,HORIZ,menuwidth); // String containing the seperator string
  220.   sep[menuwidth-1] = 0; 
  221.   // Menu title
  222.   x = (menuwidth - strlen(menu->title) - 1) >> 1;
  223.   gotoxy(top-1,left+x,ms->menupage);
  224.   printmenuitem(menu->title,ms->normalattr);
  225.   row = -1; // 1 less than inital value of x
  226.   for (x=0; x < menu->numitems; x++)
  227.     {
  228.       ci = menu->items[x];
  229.       if (ci->action == OPT_INVISIBLE) continue;
  230.       row++;
  231.       // Setup the defaults now
  232.       lchar[0] = fchar[0] = ' '; 
  233.       lchar[1] = fchar[1] = '\0'; // fchar and lchar are just spaces
  234.       str = ci->item; // Pointer to item string
  235.       attr = (x==curr ? ms->reverseattr : ms->normalattr); // Normal attributes
  236.       switch (ci->action) // set up attr,str,fchar,lchar for everything
  237.         {
  238.     case OPT_INACTIVE:
  239.       attr = (x==curr? ms->revinactattr : ms->inactattr);
  240.       break;
  241.     case OPT_SUBMENU:
  242.       lchar[0] = SUBMENUCHAR; lchar[1] = 0;
  243.       break;
  244.     case OPT_RADIOMENU:
  245.       lchar[0] = RADIOMENUCHAR; lchar[1] = 0;
  246.       break;
  247.     case OPT_CHECKBOX:
  248.       lchar[0] = (ci->itemdata.checked ? CHECKED : UNCHECKED);
  249.       lchar[1] = 0;
  250.       break;
  251.     case OPT_SEP:
  252.       fchar[0] = '\b'; fchar[1] = LTRT; fchar[2] = HORIZ; fchar[3] = HORIZ; fchar[4] = 0;
  253.       lchar[0] = HORIZ; lchar[1] = RTLT; lchar[2] = 0;
  254.       str = sep;
  255.       break;
  256.     case OPT_EXITMENU:
  257.       fchar[0] = EXITMENUCHAR; fchar[1] = 0;
  258.       break;
  259.     default: // Just to keep the compiler happy
  260.       break;
  261.         }
  262.       gotoxy(top+row,left-2,ms->menupage);
  263.       cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
  264.       gotoxy(top+row,left-2,ms->menupage);
  265.       csprint(fchar,attr[NOHLITE]); // Print first part
  266.       gotoxy(top+row,left,ms->menupage);
  267.       printmenuitem(str,attr); // Print main part
  268.       gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
  269.       csprint(lchar,attr[NOHLITE]); // Print last part
  270.     }
  271.   if (ms->handler) ms->handler(ms,menu->items[curr]);
  272. }
  273.  
  274. // Difference between this and regular menu, is that only 
  275. // OPT_INVISIBLE, OPT_SEP are honoured 
  276. void printradiomenu(pt_menu menu, int curr, char top, char left)
  277. {
  278.   int x,row; // x = index, row = position from top
  279.   int numitems,menuwidth;
  280.   char fchar[5],lchar[5]; // The first and last char in for each entry
  281.   const char *str;  // and inbetween the item or a seperator is printed
  282.   char *attr;  // all in the attribute attr
  283.   char sep[MENULEN];// and inbetween the item or a seperator is printed
  284.   pt_menuitem ci;
  285.   
  286.   calc_visible(menu);
  287.   numitems = menu->numvisible;
  288.   menuwidth = menu->menuwidth+3;
  289.   clearwindow(top,left-2, top+numitems+1, left+menuwidth+1,
  290.           ms->menupage, ms->fillchar, ms->shadowattr);
  291.   drawbox(top-1, left-3, top+numitems, left+menuwidth, 
  292.       ms->normalattr[NOHLITE], ms->menupage);
  293.   memset(sep,HORIZ,menuwidth); // String containing the seperator string
  294.   sep[menuwidth-1] = 0; 
  295.   // Menu title
  296.   x = (menuwidth - strlen(menu->title) - 1) >> 1;
  297.   gotoxy(top-1,left+x,ms->menupage);
  298.   printmenuitem(menu->title,ms->normalattr);
  299.   row = -1; // 1 less than inital value of x
  300.   for (x=0; x < menu->numitems; x++)
  301.     {
  302.       ci = menu->items[x];
  303.       if (ci->action == OPT_INVISIBLE) continue;
  304.       row++;
  305.       // Setup the defaults now
  306.       fchar[0] = RADIOUNSEL; fchar[1]='\0'; // Unselected ( )
  307.       lchar[0] = '\0'; // Nothing special after 
  308.       str = ci->item; // Pointer to item string
  309.       attr = ms->normalattr; // Always same attribute
  310.       fchar[0] = (x==curr ? RADIOSEL : RADIOUNSEL); 
  311.       switch (ci->action) // set up attr,str,fchar,lchar for everything
  312.         {
  313.     case OPT_INACTIVE:
  314.       attr = ms->inactattr;
  315.       break;
  316.     case OPT_SEP:
  317.       fchar[0] = '\b'; fchar[1] = LTRT; fchar[2] = HORIZ; fchar[3] = HORIZ; fchar[4] = 0;
  318.       lchar[0] = HORIZ; lchar[1] = RTLT; lchar[3] = 0;
  319.       str = sep;
  320.       break;
  321.     default: // To keep the compiler happy
  322.       break;
  323.         }
  324.       gotoxy(top+row,left-2,ms->menupage);
  325.       cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
  326.       gotoxy(top+row,left-2,ms->menupage);
  327.       csprint(fchar,attr[NOHLITE]); // Print first part
  328.       gotoxy(top+row,left,ms->menupage);
  329.       printmenuitem(str,attr); // Print main part
  330.       gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
  331.       csprint(lchar,attr[NOHLITE]); // Print last part
  332.     }
  333.   if (ms->handler) ms->handler(ms,menu->items[curr]);
  334. }
  335.  
  336. void cleanupmenu(pt_menu menu, char top,char left)
  337. {
  338.   clearwindow(top,left-2, top+menu->numvisible+1, left+menu->menuwidth+4,
  339.           ms->menupage, ms->fillchar, ms->fillattr); // Clear the shadow
  340.   clearwindow(top-1, left-3, top+menu->numvisible, left+menu->menuwidth+3,
  341.           ms->menupage, ms->fillchar, ms->fillattr); // main window
  342. }
  343.  
  344. /* Handle a radio menu */
  345. pt_menuitem getradiooption(pt_menu menu, char top, char left, char startopt)
  346.      // Return item chosen or NULL if ESC was hit.
  347. {
  348.   int curr,i;
  349.   char asc,scan;
  350.   char numitems;
  351.   pt_menuitem ci; // Current item
  352.     
  353.   calc_visible(menu);
  354.   numitems = menu->numvisible;
  355.   // Setup status line
  356.   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  357.   cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
  358.  
  359.   // Initialise current menu item
  360.   curr = next_visible(menu,startopt);
  361.  
  362.   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  363.   cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
  364.   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  365.   printmenuitem(menu->items[curr]->status,ms->statusattr);
  366.   while (1) // Forever
  367.     {
  368.       printradiomenu(menu,curr,top,left);
  369.       ci = menu->items[curr];
  370.       
  371.       asc = getch(&scan);
  372.       switch (scan)
  373.         {
  374.     case HOMEKEY:
  375.       curr = next_visible(menu,0);
  376.       break;
  377.     case ENDKEY:
  378.       curr = prev_visible(menu,numitems-1);
  379.       break;
  380.     case PAGEDN:
  381.       for (i=0; i < 5; i++) curr = next_visible(menu,curr+1);
  382.       break;
  383.     case PAGEUP:
  384.       for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1);
  385.       break;
  386.     case UPARROW:
  387.       curr = prev_visible(menu,curr-1);
  388.       break;
  389.     case DNARROW:
  390.       curr = next_visible(menu,curr+1);
  391.       break;
  392.     case LTARROW:
  393.     case ESCAPE:
  394.       return NULL;
  395.       break;
  396.     case RTARROW:
  397.     case ENTERA:
  398.     case ENTERB:
  399.       if (ci->action == OPT_INACTIVE) break;
  400.       if (ci->action == OPT_SEP) break;
  401.       return ci;
  402.       break;
  403.     default:
  404.       // Check if this is a shortcut key
  405.       if (((asc >= 'A') && (asc <= 'Z')) ||
  406.           ((asc >= 'a') && (asc <= 'z')) ||
  407.           ((asc >= '0') && (asc <= '9')))
  408.         curr = find_shortcut(menu,asc,curr);
  409.       break;
  410.         }
  411.       // Update status line
  412.       gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  413.       cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
  414.       printmenuitem(menu->items[curr]->status,ms->statusattr);
  415.     }
  416.   return NULL; // Should never come here
  417. }
  418.  
  419. /* Handle one menu */
  420. pt_menuitem getmenuoption( pt_menu menu, char top, char left, char startopt)
  421.      // Return item chosen or NULL if ESC was hit.
  422. {
  423.   int curr,i;
  424.   char asc,scan;
  425.   char numitems;
  426.   pt_menuitem ci; // Current item
  427.     
  428.   calc_visible(menu);
  429.   numitems = menu->numvisible;
  430.   // Setup status line
  431.   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  432.   cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
  433.  
  434.   // Initialise current menu item    
  435.   curr = next_visible(menu,startopt);
  436.  
  437.   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  438.   cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
  439.   gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  440.   printmenuitem(menu->items[curr]->status,ms->statusattr);
  441.   while (1) // Forever
  442.     {
  443.       printmenu(menu,curr,top,left);
  444.       ci = menu->items[curr];
  445.       asc = getch(&scan);
  446.       switch (scan)
  447.         {
  448.     case HOMEKEY:
  449.       curr = next_visible(menu,0);
  450.       break;
  451.     case ENDKEY:
  452.       curr = prev_visible(menu,numitems-1);
  453.       break;
  454.     case PAGEDN:
  455.       for (i=0; i < 5; i++) curr = next_visible(menu,curr+1);
  456.       break;
  457.     case PAGEUP:
  458.       for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1);
  459.       break;
  460.     case UPARROW:
  461.       curr = prev_visible(menu,curr-1);
  462.       break;
  463.     case DNARROW:
  464.       curr = next_visible(menu,curr+1);
  465.       break;
  466.     case LTARROW:
  467.     case ESCAPE:
  468.       return NULL;
  469.       break;
  470.     case RTARROW:
  471.     case ENTERA:
  472.     case ENTERB:
  473.       if (ci->action == OPT_INACTIVE) break;
  474.       if (ci->action == OPT_CHECKBOX) break;
  475.       if (ci->action == OPT_SEP) break;
  476.       if (ci->action == OPT_EXITMENU) return NULL; // As if we hit Esc
  477.       return ci;
  478.       break;
  479.     case SPACEKEY:
  480.       if (ci->action != OPT_CHECKBOX) break;
  481.       ci->itemdata.checked = !ci->itemdata.checked;
  482.       // Call handler to see it anything needs to be done
  483.       if (ci->handler != NULL) ci->handler(ms,ci); 
  484.       break;
  485.     default:
  486.       // Check if this is a shortcut key
  487.       if (((asc >= 'A') && (asc <= 'Z')) ||
  488.           ((asc >= 'a') && (asc <= 'z')) ||
  489.           ((asc >= '0') && (asc <= '9')))
  490.         curr = find_shortcut(menu,asc,curr);
  491.       break;
  492.         }
  493.       // Update status line
  494.       gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
  495.       cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
  496.       printmenuitem(menu->items[curr]->status,ms->statusattr);
  497.     }
  498.   return NULL; // Should never come here
  499. }
  500.  
  501. /* Handle the entire system of menu's. */
  502. pt_menuitem runmenusystem(char top, char left, pt_menu cmenu, char startopt, char menutype)
  503.      /*
  504.       * cmenu
  505.       *    Which menu should be currently displayed
  506.       * top,left
  507.       *    What is the position of the top,left corner of the menu
  508.       * startopt
  509.       *    which menu item do I start with
  510.       * menutype
  511.       *    NORMALMENU or RADIOMENU
  512.       *
  513.       * Return Value:
  514.       *    Returns a pointer to the final item chosen, or NULL if nothing chosen.
  515.       */
  516. {
  517.   pt_menuitem opt,choice;
  518.   char startat,mt;
  519.   char row,col;
  520.  
  521.   if (cmenu == NULL) return NULL;
  522.  startover:
  523.   if (menutype == NORMALMENU)
  524.     opt = getmenuoption(cmenu,top,left,startopt);
  525.   else // menutype == RADIOMENU
  526.     opt = getradiooption(cmenu,top,left,startopt);
  527.  
  528.   if (opt == NULL)
  529.     {
  530.       // User hit Esc
  531.       cleanupmenu(cmenu,top,left);
  532.       return NULL;
  533.     }
  534.   // Are we done with the menu system?
  535.   if ((opt->action != OPT_SUBMENU) && (opt->action != OPT_RADIOMENU)) 
  536.     {
  537.       cleanupmenu(cmenu,top,left);
  538.       return opt; // parent cleanup other menus
  539.     }
  540.   // Either radiomenu or submenu
  541.   // Do we have a valid menu number? The next hack uses the fact that 
  542.   // itemdata.submenunum = itemdata.radiomenunum (since enum data type)
  543.   if (opt->itemdata.submenunum >= ms->nummenus) // This is Bad....
  544.     {
  545.       gotoxy(12,12,ms->menupage); // Middle of screen
  546.       csprint("ERROR: Invalid submenu requested.",0x07);
  547.       cleanupmenu(cmenu,top,left);
  548.       return NULL; // Pretend user hit esc
  549.     }
  550.   // Call recursively for submenu
  551.   // Position the submenu below the current item,
  552.   // covering half the current window (horizontally)
  553.   row = ms->menus[(unsigned int)opt->itemdata.submenunum]->row;
  554.   col = ms->menus[(unsigned int)opt->itemdata.submenunum]->col;
  555.   if (row == 0xFF) row = top+opt->index+2;
  556.   if (col == 0xFF) col = left+3+(cmenu->menuwidth >> 1);
  557.   mt = (opt->action == OPT_SUBMENU ? NORMALMENU : RADIOMENU );
  558.   startat = 0;
  559.   if ((opt->action == OPT_RADIOMENU) && (opt->data != NULL))
  560.     startat = ((t_menuitem *)opt->data)->index;
  561.  
  562.   choice = runmenusystem(row, col,
  563.              ms->menus[(unsigned int)opt->itemdata.submenunum],
  564.              startat, mt );
  565.   if (opt->action == OPT_RADIOMENU)
  566.     {
  567.       if (choice != NULL) opt->data = (void *)choice; // store choice in data field
  568.       if (opt->handler != NULL) opt->handler(ms,opt); // Call handler
  569.       choice = NULL; // Pretend user hit esc
  570.     }
  571.   if (choice==NULL) // User hit Esc in submenu
  572.     {
  573.       // Startover
  574.       startopt = opt->index;
  575.       goto startover;
  576.     }
  577.   else
  578.     {
  579.       cleanupmenu(cmenu,top,left);
  580.       return choice;
  581.     }
  582. }
  583.  
  584. /* User Callable functions */
  585.  
  586. pt_menuitem showmenus(char startmenu)
  587. {
  588.   pt_menuitem rv;
  589.   char oldpage,tpos;
  590.  
  591.   // Setup screen for menusystem
  592.   oldpage = getdisppage();
  593.   setdisppage(ms->menupage);
  594.   cls();
  595.   clearwindow(ms->minrow, ms->mincol, ms->maxrow, ms->maxcol, 
  596.           ms->menupage, ms->fillchar, ms->fillattr);
  597.   tpos = (ms->numcols - strlen(ms->title) - 1) >> 1; // center it on line    
  598.   gotoxy(ms->minrow,ms->mincol,ms->menupage);
  599.   cprint(ms->tfillchar,ms->titleattr,ms->numcols,ms->menupage);
  600.   gotoxy(ms->minrow,ms->mincol+tpos,ms->menupage);
  601.   csprint(ms->title,ms->titleattr);
  602.  
  603.   cursoroff(); // Doesn't seem to work?
  604.  
  605.   // Go, main menu cannot be a radio menu 
  606.   rv = runmenusystem(ms->minrow+MENUROW, ms->mincol+MENUCOL, 
  607.              ms->menus[(unsigned int)startmenu], 0, NORMALMENU);
  608.  
  609.   // Hide the garbage we left on the screen
  610.   cursoron();
  611.   if (oldpage == ms->menupage) cls(); else setdisppage(oldpage);
  612.  
  613.   // Return user choice
  614.   return rv;
  615. }
  616.  
  617. void init_menusystem(const char *title)
  618. {
  619.   int i;
  620.     
  621.   ms = NULL;
  622.   ms = (pt_menusystem) malloc(sizeof(t_menusystem));
  623.   if (ms == NULL) return;
  624.   ms->nummenus = 0;
  625.   // Initialise all menu pointers
  626.   for (i=0; i < MAXMENUS; i++) ms->menus[i] = NULL; 
  627.     
  628.   if (title == NULL)
  629.     ms->title = TITLESTR; // Copy pointers
  630.   else ms->title = title;
  631.  
  632.   // Timeout settings
  633.   ms->tm_stepsize = TIMEOUTSTEPSIZE;
  634.   ms->tm_numsteps = TIMEOUTNUMSTEPS;
  635.  
  636.   ms->normalattr[NOHLITE] = NORMALATTR; 
  637.   ms->normalattr[HLITE] = NORMALHLITE;
  638.  
  639.   ms->reverseattr[NOHLITE] = REVERSEATTR;
  640.   ms->reverseattr[HLITE] = REVERSEHLITE;
  641.  
  642.   ms->inactattr[NOHLITE] = INACTATTR;
  643.   ms->inactattr[HLITE] = INACTHLITE;
  644.  
  645.   ms->revinactattr[NOHLITE] = REVINACTATTR;
  646.   ms->revinactattr[HLITE] = REVINACTHLITE;
  647.  
  648.   ms->statusattr[NOHLITE] = STATUSATTR;
  649.   ms->statusattr[HLITE] = STATUSHLITE;
  650.  
  651.   ms->statline = STATLINE;
  652.   ms->tfillchar= TFILLCHAR;
  653.   ms->titleattr= TITLEATTR;
  654.     
  655.   ms->fillchar = FILLCHAR;
  656.   ms->fillattr = FILLATTR;
  657.   ms->spacechar= SPACECHAR;
  658.   ms->shadowattr = SHADOWATTR;
  659.  
  660.   ms->menupage = MENUPAGE; // Usually no need to change this at all
  661.   ms->handler = NULL; // No handler function
  662.   ms->ontimeout=NULL; // No timeout handler
  663.  
  664.   // Figure out the size of the screen we are in now.
  665.   // By default we use the whole screen for our menu
  666.   ms->minrow = ms->mincol = 0;
  667.   ms->numcols = getnumcols();
  668.   ms->numrows = getnumrows();
  669.   ms->maxcol = ms->numcols - 1;
  670.   ms->maxrow = ms->numrows - 1;
  671. }
  672.  
  673. void set_normal_attr(char normal, char selected, char inactivenormal, char inactiveselected)
  674. {
  675.   if (normal != 0xFF)           ms->normalattr[0]   = normal;
  676.   if (selected != 0xFF)         ms->reverseattr[0]  = selected;
  677.   if (inactivenormal != 0xFF)   ms->inactattr[0]    = inactivenormal;
  678.   if (inactiveselected != 0xFF) ms->revinactattr[0] = inactiveselected;
  679. }
  680.  
  681. void set_normal_hlite(char normal, char selected, char inactivenormal, char inactiveselected)
  682. {
  683.   if (normal != 0xFF)           ms->normalattr[1]   = normal;
  684.   if (selected != 0xFF)         ms->reverseattr[1]  = selected;
  685.   if (inactivenormal != 0xFF)   ms->inactattr[1]    = inactivenormal;
  686.   if (inactiveselected != 0xFF) ms->revinactattr[1] = inactiveselected;
  687. }
  688.  
  689.  
  690. void set_status_info(char statusattr, char statushlite, char statline)
  691. {
  692.   if (statusattr != 0xFF) ms->statusattr[NOHLITE] = statusattr;
  693.   if (statushlite!= 0xFF) ms->statusattr[HLITE] = statushlite;
  694.   // statline is relative to minrow
  695.   if (statline >= ms->numrows) statline = ms->numrows - 1;
  696.   ms->statline = statline; // relative to ms->minrow, 0 based
  697. }
  698.  
  699. void set_title_info(char tfillchar, char titleattr)
  700. {
  701.   if (tfillchar  != 0xFF) ms->tfillchar  = tfillchar;
  702.   if (titleattr  != 0xFF) ms->titleattr  = titleattr;
  703. }
  704.  
  705. void set_misc_info(char fillchar, char fillattr,char spacechar, char shadowattr)
  706. {
  707.   if (fillchar  != 0xFF) ms->fillchar  = fillchar;
  708.   if (fillattr  != 0xFF) ms->fillattr  = fillattr;
  709.   if (spacechar != 0xFF) ms->spacechar = spacechar;
  710.   if (shadowattr!= 0xFF) ms->shadowattr= shadowattr;
  711. }
  712.  
  713. void set_window_size(char top, char left, char bot, char right) // Set the window which menusystem should use
  714. {
  715.     
  716.   char nr,nc;
  717.   if ((top > bot) || (left > right)) return; // Sorry no change will happen here
  718.   nr = getnumrows();
  719.   nc = getnumcols();
  720.   if (bot >= nr) bot = nr-1;
  721.   if (right >= nc) right = nc-1;
  722.   ms->minrow = top;
  723.   ms->mincol = left;
  724.   ms->maxrow = bot;
  725.   ms->maxcol = right;
  726.   ms->numcols = right - left + 1;
  727.   ms->numrows = bot - top + 1;
  728.   if (ms->statline >= ms->numrows) ms->statline = ms->numrows - 1; // Clip statline if need be
  729. }
  730.  
  731. void reg_handler( t_menusystem_handler handler)
  732. {
  733.   ms->handler = handler;
  734. }
  735.  
  736. void unreg_handler()
  737. {
  738.   ms->handler = NULL;
  739. }
  740.  
  741. void reg_ontimeout(t_timeout_handler handler, unsigned int numsteps, unsigned int stepsize)
  742. {
  743.   ms->ontimeout = handler;
  744.   if (numsteps != 0) ms->tm_numsteps = numsteps;
  745.   if (stepsize != 0) ms->tm_stepsize = stepsize;
  746. }
  747.  
  748. void unreg_ontimeout()
  749. {
  750.   ms->ontimeout = NULL;
  751. }
  752.  
  753.  
  754. void calc_visible(pt_menu menu)
  755. {
  756.   int ans,i;
  757.  
  758.   if (menu == NULL) return;  
  759.   ans = 0;
  760.   for (i=0; i < menu->numitems; i++)
  761.     if (menu->items[i]->action != OPT_INVISIBLE) ans++;
  762.   menu->numvisible = ans;
  763. }
  764.  
  765. char add_menu(const char *title) // Create a new menu and return its position
  766. {
  767.   int num,i;
  768.   pt_menu m;
  769.  
  770.   num = ms->nummenus;
  771.   if (num >= MAXMENUS) return -1;
  772.   m = NULL;
  773.   m = (pt_menu) malloc(sizeof(t_menu));
  774.   if (m == NULL) return -1;
  775.   ms->menus[num] = m;
  776.   m->numitems = 0;
  777.   m->row = 0xFF;
  778.   m->col = 0xFF;
  779.   for (i=0; i < MAXMENUSIZE; i++) m->items[i] = NULL;
  780.    
  781.   if (title)
  782.     {
  783.       if (strlen(title) > MENULEN - 2)
  784.     m->title = TITLELONG;
  785.       else m->title = title; 
  786.     }
  787.   else m->title = EMPTYSTR; 
  788.   m ->menuwidth = strlen(m->title);
  789.   ms->nummenus ++;
  790.   return ms->nummenus - 1;
  791. }
  792.  
  793. void set_menu_pos(char row,char col) // Set the position of this menu.
  794. {
  795.   pt_menu m;
  796.  
  797.   m = ms->menus[ms->nummenus-1];
  798.   m->row = row;
  799.   m->col = col;
  800. }
  801.  
  802. pt_menuitem add_sep() // Add a separator to current menu
  803. {
  804.   pt_menuitem mi;
  805.   pt_menu m;
  806.  
  807.   m = (ms->menus[ms->nummenus-1]);
  808.   mi = NULL;
  809.   mi = (pt_menuitem) malloc(sizeof(t_menuitem));
  810.   if (mi == NULL) return NULL;
  811.   m->items[(unsigned int)m->numitems] = mi;
  812.   mi->handler = NULL; // No handler
  813.   mi->item = mi->status = mi->data = EMPTYSTR;
  814.   mi->action = OPT_SEP;
  815.   mi->index = m->numitems++;
  816.   mi->parindex = ms->nummenus-1;
  817.   mi->shortcut = 0;
  818.   return mi;
  819. }
  820.  
  821. // Add item to the "current" menu
  822. pt_menuitem add_item(const char *item, const char *status, t_action action, 
  823.              const char *data, char itemdata) 
  824. {
  825.   pt_menuitem mi;
  826.   pt_menu m;
  827.   const char *str;
  828.   char inhlite=0; // Are we inside hlite area
  829.  
  830.   m = (ms->menus[ms->nummenus-1]);
  831.   mi = NULL;
  832.   mi = (pt_menuitem) malloc(sizeof(t_menuitem));
  833.   if (mi == NULL) return NULL;
  834.   m->items[(unsigned int) m->numitems] = mi;
  835.   mi->handler = NULL; // No handler
  836.   if (item) {
  837.     if (strlen(item) > MENULEN - 2) {
  838.       mi->item = ITEMLONG; 
  839.     } else {
  840.       mi->item = item; 
  841.       if (strlen(item) > m->menuwidth) m->menuwidth = strlen(item);
  842.     }
  843.   } else mi->item = EMPTYSTR; 
  844.  
  845.   if (status) {
  846.     if (strlen(status) > STATLEN - 2) {
  847.       mi->status = STATUSLONG; 
  848.     } else {
  849.       mi->status = status; 
  850.     }
  851.   } else mi->status = EMPTYSTR; 
  852.     
  853.   mi->action = action;
  854.   str = mi->item;
  855.   mi->shortcut = 0;
  856.   inhlite = 0; // We have not yet seen an ENABLEHLITE char
  857.   // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char
  858.   while (*str)
  859.     {
  860.       if (*str == ENABLEHLITE) 
  861.     {
  862.       inhlite=1;
  863.     }
  864.       if (*str == DISABLEHLITE)
  865.     {
  866.       inhlite = 0;
  867.     }
  868.       if ( (inhlite == 1) && 
  869.        (((*str >= 'A') && (*str <= 'Z')) || 
  870.         ((*str >= 'a') && (*str <= 'z')) ||
  871.         ((*str >= '0') && (*str <= '9'))))
  872.     {
  873.       mi->shortcut=*str;
  874.       break;
  875.     }
  876.       ++str;
  877.     }
  878.   if ((mi->shortcut >= 'A') && (mi->shortcut <= 'Z')) // Make lower case
  879.     mi->shortcut = mi->shortcut -'A'+'a';
  880.  
  881.   if (data) {
  882.     if (strlen(data) > ACTIONLEN - 2) {
  883.       mi->data = ACTIONLONG; 
  884.     } else {
  885.       mi->data = data; 
  886.     }
  887.   } else mi->data = EMPTYSTR;
  888.  
  889.   switch (action)
  890.     {
  891.     case OPT_SUBMENU:
  892.       mi->itemdata.submenunum = itemdata;
  893.       break;
  894.     case OPT_CHECKBOX:
  895.       mi->itemdata.checked = itemdata;
  896.       break;
  897.     case OPT_RADIOMENU:
  898.       mi->itemdata.radiomenunum = itemdata;
  899.       mi->data = NULL; // No selection made
  900.       break;
  901.     default: // to keep the compiler happy
  902.       break;
  903.     }
  904.   mi->index = m->numitems++;
  905.   mi->parindex = ms->nummenus-1;
  906.   return mi;
  907. }
  908.  
  909. // Set the shortcut key for the current item
  910. void set_shortcut(char shortcut)
  911. {
  912.   pt_menuitem mi;
  913.   pt_menu m;
  914.  
  915.   m = (ms->menus[ms->nummenus-1]); 
  916.   if (m->numitems <= 0) return;
  917.   mi = m->items[(unsigned int) m->numitems-1];
  918.   mi->shortcut = shortcut;
  919. }
  920.